<?php

namespace App\Services\AetherUpload\Lib;

use App\Services\AetherUpload\Eloquent\DriverResource;

class PartialResource
{
    // public $group;
    public $tempName;
    public $groupDir;
    public $groupSubDir;
    public $path;
    public $header;
    private $driver;
    private $chunkIndex = null;

    /**
     * @param $tempBaseName
     * @param $extension
     * @param $groupSubDir
     * @throws \Exception
     */
    public function __construct($tempBaseName, $extension, $groupSubDir)
    {
        $this->tempName = Util::getFileName($tempBaseName, $extension);
        $this->groupDir = ConfigMapper::get('group_dir');
        $this->groupSubDir = $groupSubDir;
        $this->path = $this->getPath();

        $this->driver = DriverResource::getInstance($this->getPath(), ConfigMapper::get('group_storage_disk'));
        $this->header = new Header($tempBaseName);

    }

    private function getPath(): string
    {
        if (!$this->path) {
            $this->path = trim($this->groupDir . '/' . $this->groupSubDir, '/') . '/' . $this->tempName . '.part';
        }
        return $this->path;
    }

    public function has($path): bool
    {
        return $this->driver->has($path);
    }

    public function exists(): bool
    {
        return $this->driver->exists($this->getPath());
    }

    public function create($path, $content)
    {
        if ($this->driver->put($path, $content) === false) {
            throw new \Exception(Message::message('create_resource_fail'));
        }
        return $path;
    }

    public function append($chunkRealPath, $chunkIndex)
    {
        $handle = @fopen($chunkRealPath, 'rb');

        $chunkIndex = (int)$chunkIndex;
        $size = $this->chunkSize();
        $position = $size * ($chunkIndex - 1);
        if ($position < 0) {
            throw new \Exception(Message::message('write_resource_fail'));
        }
        if ($this->driver->append($this->path, $handle, $position) === false) {
            throw new \Exception(Message::message('write_resource_fail'));
        }

        fclose($handle);
    }

    private function chunkSize(): int
    {
        return (int)ConfigMapper::get('chunk_size');
    }

    /**
     * @return void
     * @throws \Exception
     */
    public function delete()
    {
        $path = $this->path;
        if ($this->driver->exists($path)) {
            if ($this->driver->delete($path) === false) {
                throw new \Exception(Message::message('delete_resource_fail'));
            }
        }
    }

    public function getCompletePath($completeName): string
    {
        return dirname($this->path) . '/' . $completeName;
    }

    public function hasCompleteName($completeName): bool
    {
        return $this->driver->exists($this->getCompletePath($completeName));
    }

    public function rename($completeName): string
    {
        $completePath = $this->getCompletePath($completeName);
        if ($this->driver->exists($completePath) === false) {
            if ($this->driver->move($this->path, $completePath) === false) {
                throw new \Exception(Message::message('rename_resource_fail'));
            }
        } else {
            $this->driver->delete($this->path);
        }
        return $completePath;
    }

    public function calculateHash()
    {
        return $this->driver->calculateHash();
    }

    public function filterBySize($resourceSize)
    {
        $maxSize = (int)ConfigMapper::get('resource_maxsize');

        if ((int)$resourceSize === 0 || ((int)$resourceSize > $maxSize && $maxSize !== 0)) {
            throw new \Exception(Message::message('invalid_resource_size'));
        }

    }

    public function filterByExtension($resourceExt)
    {
        $extensions = ConfigMapper::get('resource_extensions');

        if (empty($resourceExt) || (empty($extensions) === false && in_array($resourceExt, $extensions) === false) || in_array($resourceExt, ConfigMapper::get('forbidden_extensions')) === true) {
            throw new \Exception(Message::message('invalid_resource_type'));
        }
    }

    public function checkSize()
    {
        $this->filterBySize($this->driver->size());
    }

    public function checkMimeType()
    {
        $extension = MimeType::search($this->driver->mimeType());

        if ($extension === null) {
            throw new \Exception(Message::message('missing_mimetype'));
        }

        $this->filterByExtension($extension);
    }


    public function __set($property, $value)
    {
        if ($property === 'chunkIndex') {
            $this->header->write($value);
        }
    }

    public function __get($property)
    {
        if ($property === 'chunkIndex') {
            return $this->header->read();
        }

        return null;
    }

    public function __unset($property)
    {
        if ($property === 'chunkIndex') {
            $this->header->delete();
        }
    }

    public function __call($name, $arguments)
    {
        if (method_exists($this->driver, $name)) {
            return call_user_func_array([$this->driver, $name], $arguments);
        }

        return null;
    }


}
